#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>

#include "_font.h"
#include "_gfx.h"
#include "_window.h"
#include "icon.h"
#include "button.h"
#include "editbox.h"
#include "multieditbox.h"
#include "image.h"
#include "label.h"
#include "window.h"
#include "listbox.h"
#include "listener.h"
#include "_osd.h"

#define __DEBUG__

#ifdef __DEBUG__
#define __E(fmt, args...) fprintf(stderr, "WINDOW ERROR [%d]: " fmt, __LINE__, ## args)
#define __W(fmt, args...) fprintf(stderr, "WINDOW WARNING [%d]: " fmt, __LINE__, ## args)
#else
#define __E(fmt, args...)
#define __W(fmt, args...)
#endif

static unsigned int window_scanArea(signed int windowId, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
static unsigned int window_rectangleIntersects(unsigned int x1, unsigned int y1, unsigned int w1, unsigned int h1,
        unsigned int x2, unsigned int y2, unsigned int w2, unsigned int h2);

extern unsigned short *rgb_buffers[OSD_NUM_BUFS];
extern unsigned char *attr_buffers[ATTR_NUM_BUFS];
extern int osd_size;
extern int attr_size;

static unsigned char rgb_actualBuf = 0;
static unsigned char attr_actualBuf = 0;

static window windows[WINDOW_MAX_NUMBER];
static signed int actual_window_selected = -1;
static signed int actual_window_enable = -1;

//#define TEMPOS

#ifdef TEMPOS
#include <utils/timerutil.h>
static TimerUtilObj timer;
static unsigned long time;

static TimerUtilObj timer2;
static unsigned long time2;
#endif

signed int window_create(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
	static unsigned char initialized = 0;
    signed int winId = -1;

    signed int i;
    for (i = 0; i < WINDOW_MAX_NUMBER; i++) {
        if (windows[i].initialized == 0) {
            winId = i;
            break;
        }
    }

    if (!initialized) {
        
		if (osd_init() == OSD_FAILURE) {
            __E("Falha ao inicializar o OSD.\n");
            return WINDOW_FAILURE;
        }

        if (attr_init() == OSD_FAILURE) {
            __E("Falha ao inicializar o atributo do OSD.\n");
            return WINDOW_FAILURE;
        }

		font_init(FONT_FILE);

    } else if (winId == WINDOW_FAILURE) {
        __W("Nao e possivel criar mais telas. Numero maximo.\n");
        return WINDOW_FAILURE;
    }

    windows[winId].x = x;
    windows[winId].y = y;
    windows[winId].h = height;
    windows[winId].w = width;
    windows[winId].visible = 0;
    windows[winId].firstButton = -1;
    windows[winId].actualButton = -1;
    windows[winId].actualButton_selected = -1;
    windows[winId].firstIcon = -1;
    windows[winId].actualIcon = -1;
    windows[winId].actualIcon_selected = -1;
    windows[winId].firstEditbox = -1;
    windows[winId].actualEditbox = -1;
    windows[winId].actualEditbox_selected = -1;
    windows[winId].firstMultieditbox = -1;
    windows[winId].actualMultieditbox = -1;
    windows[winId].firstImage = -1;
    windows[winId].actualImage = -1;
    windows[winId].firstLabel = -1;
    windows[winId].actualLabel = -1;
    windows[winId].backgroundImageId = -1;
    windows[winId].firstListbox = -1;
    windows[winId].actualListbox = -1;
    windows[winId].firstListener = -1;
    windows[winId].actualListener = -1;
    windows[winId].backgroundColor = -1;
    windows[winId].backgroundColorVisible = 0;
    windows[winId].backgroundColorRedraw = 1;

    windows[winId].rgbBuffer = (unsigned short *) malloc(windows[winId].h * windows[winId].w * OSD_BITS_PER_PIXEL / 8);
    windows[winId].attrBuffer = (unsigned char *) malloc(windows[winId].h * windows[winId].w / ATTR_PIXELS_PER_BYTE);

    if (windows[winId].rgbBuffer == NULL || windows[winId].attrBuffer == NULL) {
        __E("Nao foi possivel criar a tela.\n");
        return WINDOW_FAILURE;
    }

    memset(windows[winId].rgbBuffer, 0x00, windows[winId].h * windows[winId].w * 2);
    memset(windows[winId].attrBuffer, 0x00, windows[winId].h * windows[winId].w / 2);

    windows[winId].initialized = 1;
	initialized = 1;
    return winId;
}

signed int window_destroy(signed int windowId) {
    if (!windows[windowId].initialized) {
        __E("Janela nao pode ser destruida: janela nao foi criada.");
        return WINDOW_FAILURE;
    }

    windows[windowId].x = 0;
    windows[windowId].y = 0;
    windows[windowId].w = 0;
    windows[windowId].h = 0;

    if (windows[windowId].firstButton != -1) {
        do {
            button_destroy(windows[windowId].firstButton);
        } while (windows[windowId].firstButton != -1);
    }

    if (windows[windowId].firstIcon != -1) {
        do {
            icon_destroy(windows[windowId].firstIcon);
        } while (windows[windowId].firstIcon != -1);
    }

    if (windows[windowId].firstMultieditbox != -1) {
        do {
            multieditbox_destroy(windows[windowId].firstMultieditbox);
        } while (windows[windowId].firstMultieditbox != -1);
    }

    if (windows[windowId].firstEditbox != -1) {
        do {
            editbox_destroy(windows[windowId].firstEditbox);
        } while (windows[windowId].firstEditbox != -1);
    }

    if (windows[windowId].firstImage != -1) {
        do {
            image_destroy(windows[windowId].firstImage);
        } while (windows[windowId].firstImage != -1);
    }

    if (windows[windowId].firstListbox != -1) {
        do {
            listbox_destroy(windows[windowId].firstListbox);
        } while (windows[windowId].firstListbox != -1);
    }

    if (windows[windowId].firstLabel != -1) {
        do {
            label_destroy(windows[windowId].firstLabel);
        } while (windows[windowId].firstLabel != -1);
    }

    if (windows[windowId].firstListener != -1) {
        do {
            listener_destroy(windows[windowId].firstListener);
        } while (windows[windowId].firstListener != -1);
    }

    windows[windowId].backgroundImageId = -1;
    windows[windowId].backgroundColorVisible = 0;

    free(windows[windowId].rgbBuffer);
    free(windows[windowId].attrBuffer);

    windows[windowId].backgroundColor = 0;
    windows[windowId].initialized = 0;
    windows[windowId].visible = 0;
    windows[windowId].backgroundColorRedraw = 0;

    return WINDOW_SUCCESS;
}

signed int window_draw(signed int windowId) {
    // Atualiza o buffer que nao esta sendo exibido.
    // Depois deve-se chamar window_refresh para fazer a exibicao atualizada do buffer.
    unsigned short *dst = rgb_buffers[!rgb_actualBuf] + (windows[windowId].y * D1_WIDTH + windows[windowId].x);
    unsigned short *src = windows[windowId].rgbBuffer;
    unsigned char *srcAttr = windows[windowId].attrBuffer;
    unsigned char *dstAttr = attr_buffers[!attr_actualBuf] + ((windows[windowId].y * 768 + windows[windowId].x) / ATTR_PIXELS_PER_BYTE);

#ifdef TEMPOS
    TimerUtil_reset(&timer);
#endif

    //memset(windows[windowId].rgbBuffer, 0, windows[windowId].w * windows[windowId].h * OSD_BITS_PER_PIXEL / 8);
    //memset(windows[windowId].attrBuffer, 0, windows[windowId].w * windows[windowId].h / ATTR_PIXELS_PER_BYTE);

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("memset: %d\n", time);
#endif

    if (windows[windowId].backgroundColorVisible && windows[windowId].backgroundColorRedraw) {
        wmemset(windows[windowId].rgbBuffer, ((((unsigned int) (windows[windowId].backgroundColor)) << 16) | windows[windowId].backgroundColor), windows[windowId].h * windows[windowId].w / 2);
        memset(windows[windowId].attrBuffer, MAX_TRANSPARENCY, windows[windowId].w * windows[windowId].h / ATTR_PIXELS_PER_BYTE);
        windows[windowId].backgroundColorRedraw = 0;
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("background color: %d\n", time);
#endif

    if (window_drawBackground(windowId) == WINDOW_FAILURE) {
        __E("Nao foi possivel exibir o fundo da tela.\n");
        return WINDOW_FAILURE;
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("background image: %d\n", time);
#endif

    if (windows[windowId].firstImage != -1) {
        signed int aux = windows[windowId].firstImage;
        do {
            image_draw(aux);
            aux = image_next(aux);
        } while (windows[windowId].firstImage != aux);
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("images: %d\n", time);
#endif

    if (windows[windowId].firstButton != -1) {
        signed int aux = windows[windowId].firstButton;
        do {
            button_draw(aux);
            aux = button_next(aux);
        } while (windows[windowId].firstButton != aux);
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("buttons: %d\n", time);
#endif

    if (windows[windowId].firstEditbox != -1) {
        signed int aux = windows[windowId].firstEditbox;
        do {
            editbox_draw(aux);
            aux = editbox_next(aux);
        } while (windows[windowId].firstEditbox != aux);
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("editbox: %d\n", time);
#endif

    if (windows[windowId].firstLabel != -1) {
        signed int aux = windows[windowId].firstLabel;
        do {
            label_draw(aux);
            aux = label_next(aux);
        } while (windows[windowId].firstLabel != aux);
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("label: %d\n", time);
#endif

    if (windows[windowId].firstListbox != -1) {
        signed int aux = windows[windowId].firstListbox;
        do {
            listbox_draw(aux);
            aux = listbox_next(aux);
        } while (windows[windowId].firstListbox != aux);
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("listbox: %d\n", time);
#endif

    unsigned char par = ((windows[windowId].y * D1_WIDTH + windows[windowId].x) % 2) ? 0 : 1;
    unsigned char largura_par = (windows[windowId].w % 2) ? 0 : 1;
    unsigned int largura = windows[windowId].w <= (D1_WIDTH - windows[windowId].x) ? windows[windowId].w : (D1_WIDTH - windows[windowId].x);

    signed int i;
    for (i = 0; i < windows[windowId].h && i < (D1_HEIGHT - windows[windowId].y); i++) {
        if (par && !largura_par) {
            // par   - largura ímpar -> corrigir último
            //printf("par && !largura_par\n");
            memcpy(dstAttr, srcAttr, ((largura / 2) - 1));
            *(dstAttr + (largura / 2)) &= 0x0f;
            *(dstAttr + (largura / 2)) |= *(srcAttr + (largura / 2)) & 0xf0;
        } else if (!par && largura_par) {
            // ímpar - largura par   -> corrigir ambos
            //printf("!par && largura_par\n");
            *dstAttr &= 0xf0;
            *dstAttr |= *srcAttr & 0x0f;
            memcpy((dstAttr + 1), (srcAttr + 1), ((largura / 2) - 2));
            *(dstAttr + (largura / 2)) &= 0x0f;
            *(dstAttr + (largura / 2)) |= *(srcAttr + (largura / 2)) & 0xf0;
        } else if (!par & !largura_par) {
            // ímpar - largura ímpar -> corrigir primeiro
            //printf("!par & !largura_par\n");
            *dstAttr &= 0xf0;
            *dstAttr |= *srcAttr & 0x0f;
            memcpy((dstAttr + 1), (srcAttr + 1), ((largura / 2) - 1));
        } else if (par && largura_par) {
            // par   - largura par   -> certo
            //printf("par & largura_par\n");
            memcpy(dstAttr, srcAttr, largura / 2);
        }

        memcpy(dstAttr, srcAttr, largura / 2);
        memcpy(dst, src, largura * 2);

        dst += D1_WIDTH;
        src += windows[windowId].w;
        dstAttr += 768 / ATTR_PIXELS_PER_BYTE;
        srcAttr += windows[windowId].w / ATTR_PIXELS_PER_BYTE;
    }

#ifdef TEMPOS
    TimerUtil_delta(&timer, &time);
    printf("renderizaçao: %d\n", time);
#endif

    return WINDOW_SUCCESS;
}

signed int window_setVisibility(signed int windowId, unsigned char visibility) {
    if (windows[windowId].initialized) {
        windows[windowId].visible = visibility;
        return WINDOW_SUCCESS;
    } else {
        __E("Tela ainda nao foi criada.\n");

        return WINDOW_FAILURE;
    }
}

/* window_changeActual faz apenas a janela indicada por windowId ser visível,
   escondendo todas as outras, e tambem seta a janela como a atual tratadora
   de eventos */
signed int window_changeActual(signed int windowId) {
    signed int i;

    for (i = 0; i < WINDOW_MAX_NUMBER; i++) {
        if (windows[i].visible) {
            window_setVisibility(i, 0);
        }
    }

    if (window_setVisibility(windowId, 1) == WINDOW_FAILURE) {
        __E("Erro ao alterar a janela ativa\n");
        return WINDOW_FAILURE;
    }

    if (window_setEnable(windowId) == WINDOW_FAILURE) {
        __E("Erro ao alterar a janela ativa\n");
        return WINDOW_FAILURE;
    }

    return WINDOW_SUCCESS;
}

/* window_refresh chaveia entre a exibicao do primeiro e do segundo buffer */
void window_refresh() {
    signed int i;
	
    memset(rgb_buffers[!rgb_actualBuf], 0x00, osd_size * OSD_BITS_PER_PIXEL / 8);
    memset(attr_buffers[!attr_actualBuf], NO_TRANSPARENCY, attr_size / ATTR_PIXELS_PER_BYTE);

    for (i = 0; i < WINDOW_MAX_NUMBER; i++) {
        if (windows[i].visible) {
            window_draw(i);
        }
    }

    rgb_actualBuf = !rgb_actualBuf;
    attr_actualBuf = !attr_actualBuf;

#ifdef TEMPOS
    TimerUtil_reset(&timer2);
#endif

    osd_setActual_rgbBuffer(rgb_actualBuf);
    osd_setActual_attrBuffer(attr_actualBuf);

#ifdef TEMPOS
    TimerUtil_delta(&timer2, &time2);
    printf("chaveamento: %d\n", time2);
#endif
}

signed int window_setBackground(signed int windowId, unsigned char *filename) {
	if ((windows[windowId].backgroundImageId = image_create(0, 0, filename)) == IMAGE_FAILURE) {
        __E("Erro ao criar imagem de fundo da janela\n");
        return WINDOW_FAILURE;
    }

    if (window_insertImage(windowId, windows[windowId].backgroundImageId) == WINDOW_FAILURE) {
        __E("Erro ao inserir imagem de fundo da janela\n");
        return WINDOW_FAILURE;
    }

    return WINDOW_SUCCESS;
}

signed int window_setBackgroundColor(signed int windowId, unsigned char r, unsigned char g, unsigned char b) {
    if (windows[windowId].initialized) {
        windows[windowId].backgroundColor = color_convert(r, g, b);
        windows[windowId].backgroundColorVisible = 1;
        return WINDOW_SUCCESS;
    } else {
        __E("Tela ainda nao foi criada.\n");

        return WINDOW_FAILURE;
    }
}

signed int window_setBackgroundVisibility(signed int windowId, unsigned char visibility) {
    if (windows[windowId].initialized) {
        if (image_setVisible(windows[windowId].backgroundImageId, visibility) == IMAGE_FAILURE) {
            __E("Não foi possível definir a visibilidade da imagem de fundo.\n");
            return WINDOW_FAILURE;
        } else return WINDOW_SUCCESS;
    } else {
        __E("Tela ainda nao foi criada.\n");

        return WINDOW_FAILURE;
    }
}

signed int window_drawBackground(signed int windowId) {
    if (windows[windowId].backgroundImageId != -1) {
        if (image_draw(windows[windowId].backgroundImageId) == IMAGE_FAILURE) {
            __E("Nao foi possivel desenhar a imagem de fundo.\n");
            return WINDOW_FAILURE;
        } 
    }
    return WINDOW_SUCCESS;
}

signed int window_getHeight(signed int windowId) {
    if (windows[windowId].initialized) {
        return windows[windowId].h;
    } else {
        __E("Tela ainda nao foi criada.\n");

        return WINDOW_FAILURE;
    }
}

signed int window_getWidth(signed int windowId) {
    if (windows[windowId].initialized) {
        return windows[windowId].w;
    } else {
        __E("Tela ainda nao foi criada.\n");

        return WINDOW_FAILURE;
    }
}

signed int window_setEnable(signed int windowId) {
    if (windows[windowId].initialized) {
        actual_window_enable = windowId;
        return WINDOW_SUCCESS;
    } else return WINDOW_FAILURE;
}

signed int window_getEnable() {
    return actual_window_enable;
}

signed int window_insertButton(signed int windowId, signed int buttonId) {
    signed int actual_button, first_button;

    if (window_scanArea(windowId, button_getX(buttonId), button_getY(buttonId), button_getWidth(buttonId),
            button_getHeight(buttonId))) {
        __E("O botão não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstButton == -1) {
        actual_button = buttonId;
        first_button = buttonId;
    } else {
        actual_button = windows[windowId].actualButton;
        first_button = windows[windowId].firstButton;
    }

    if (button_addToWindow(actual_button, buttonId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o botao na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualButton = buttonId;
    windows[windowId].firstButton = first_button;

    return WINDOW_SUCCESS;
}

signed int window_set_actualButton_selected(signed int windowId, signed int buttonId) {
    if (windowId >= 0) {
        windows[windowId].actualButton_selected = buttonId;
        return WINDOW_SUCCESS;
    } else return WINDOW_FAILURE;
}

signed int window_inform_actualButton(signed int windowId) {
    if (windows[windowId].actualButton == -1) {
        return WINDOW_FAILURE;
    } else {
        return windows[windowId].actualButton;
    }
}

signed int window_inform_actualButton_selected(signed int windowId) {
    if (windows[windowId].actualButton_selected == -1) {
        return WINDOW_FAILURE;
    }

    if (button_isVisible(windows[windowId].actualButton_selected) == 1) {
        return windows[windowId].actualButton_selected;
    } else return WINDOW_FAILURE;
}

signed int window_previousButton(signed int windowId) {
    if (window_inform_actualButton_selected(windowId) == -1) {
        button_select(windows[windowId].firstButton);
        return windows[windowId].actualButton_selected;
    } else {
        signed int previous_buttonId = button_previous(windows[windowId].actualButton_selected);
        signed int aux = previous_buttonId;
        if (button_isBlocked(previous_buttonId)) {
            do {
                previous_buttonId = button_previous(previous_buttonId);
            } while (button_isBlocked(previous_buttonId) && previous_buttonId != aux);
        }
        button_select(previous_buttonId);

        return windows[windowId].actualButton_selected;
    }
}

signed int window_nextButton(signed int windowId) {
    if (window_inform_actualButton_selected(windowId) == -1) {
        button_select(windows[windowId].firstButton);
        return windows[windowId].actualButton_selected;
    } else {
        signed int next_buttonId = button_next(windows[windowId].actualButton_selected);
        signed int aux = next_buttonId;
        if (button_isBlocked(next_buttonId)) {
            do {
                next_buttonId = button_next(next_buttonId);
            } while (button_isBlocked(next_buttonId) && next_buttonId != aux);
        }
        button_select(next_buttonId);

        return windows[windowId].actualButton_selected;
    }
}

signed int window_insertIcon(signed int windowId, signed int iconId) {
    signed int actual_icon, first_icon;

    if (window_scanArea(windowId, icon_getX(iconId), icon_getY(iconId), icon_getWidth(iconId),
            icon_getHeight(iconId))) {
        __E("O ícone não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstIcon == -1) {
        actual_icon = iconId;
        first_icon = iconId;
    } else {
        actual_icon = windows[windowId].actualIcon;
        first_icon = windows[windowId].firstIcon;
    }

    if (icon_addToWindow(actual_icon, iconId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o ícone na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualIcon = iconId;
    windows[windowId].firstIcon = first_icon;

    return WINDOW_SUCCESS;
}

signed int window_set_actualIcon_selected(signed int windowId, signed int iconId) {
    if (windowId >= 0) {
        windows[windowId].actualIcon_selected = iconId;
        return WINDOW_SUCCESS;
    } else return WINDOW_FAILURE;
}

signed int window_inform_actualIcon(signed int windowId) {
    if (windows[windowId].actualIcon == -1) {
        return WINDOW_FAILURE;
    } else {
        return windows[windowId].actualIcon;
    }
}

signed int window_inform_actualIcon_selected(signed int windowId) {
    if (windows[windowId].actualIcon_selected == -1) {
        return WINDOW_FAILURE;
    }

    if (icon_isSelected(windows[windowId].actualIcon_selected) == 1) {
        return windows[windowId].actualIcon_selected;
    } else {
        return WINDOW_FAILURE;
    }
}

signed int window_previousIcon(signed int windowId) {
    if (window_inform_actualIcon_selected(windowId) == -1) {
        icon_select(windows[windowId].firstIcon);
        return windows[windowId].actualIcon_selected;
    } else {
        icon_select(icon_previous(windows[windowId].actualIcon_selected));
        return windows[windowId].actualIcon_selected;
    }
}

signed int window_nextIcon(signed int windowId) {
    if (window_inform_actualIcon_selected(windowId) == -1) {
        icon_select(windows[windowId].firstIcon);
        return windows[windowId].actualIcon_selected;
    } else {
        icon_select(icon_next(windows[windowId].actualIcon_selected));
        return windows[windowId].actualIcon_selected;
    }
}

signed int window_insertEditbox(signed int windowId, signed int editboxId) {
    signed int actual_editbox, first_editbox;

    if (window_scanArea(windowId, editbox_getX(editboxId), editbox_getY(editboxId), editbox_getWidth(editboxId),
            editbox_getHeight(editboxId))) {
        __E("O editbox não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstEditbox == -1) {
        actual_editbox = editboxId;
        first_editbox = editboxId;
    } else {
        actual_editbox = windows[windowId].actualEditbox;
        first_editbox = windows[windowId].firstEditbox;
    }

    if (editbox_addToWindow(actual_editbox, editboxId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o editbox na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualEditbox = editboxId;
    windows[windowId].firstEditbox = first_editbox;

    return WINDOW_SUCCESS;
}

signed int window_set_actualEditbox_selected(signed int windowId, signed int editboxId) {
    if (windowId >= 0) {
        windows[windowId].actualEditbox_selected = editboxId;
        return WINDOW_SUCCESS;
    } else return WINDOW_FAILURE;
}

signed int window_inform_actualEditbox(signed int windowId) {
    if (windows[windowId].actualEditbox == -1) {
        return WINDOW_FAILURE;
    } else {
        return windows[windowId].actualEditbox;
    }
}

signed int window_inform_actualEditbox_selected(signed int windowId) {
    if (windows[windowId].actualEditbox_selected == -1) {
        return WINDOW_FAILURE;
    }

    if (editbox_read(windows[windowId].actualEditbox_selected) == 1) {
        return windows[windowId].actualEditbox_selected;

    } else {
        return WINDOW_FAILURE;
    }
}

signed int window_previousEditbox(signed int windowId) {
    if (window_inform_actualEditbox_selected(windowId) == WINDOW_FAILURE) {
        editbox_select(windows[windowId].actualEditbox);
        return windows[windowId].actualEditbox_selected;
    } else {
        editbox_select(editbox_previous(windows[windowId].actualEditbox_selected));

        return windows[windowId].actualEditbox_selected;
    }
}

signed int window_nextEditbox(signed int windowId) {
    if (window_inform_actualEditbox_selected(windowId) == -1) {
        editbox_select(windows[windowId].firstEditbox);
        return windows[windowId].actualEditbox_selected;
    } else {
        editbox_select(editbox_next(windows[windowId].actualEditbox_selected));
        return windows[windowId].actualEditbox_selected;
    }
}

signed int window_insertListener(signed int windowId, signed int listenerId) {
    if (windows[windowId].firstListener == -1) {
        if (listener_addToWindow(listenerId, listenerId, windowId) == LISTENER_FAILURE) {
            __E("Nao foi possivel inserir o listener na tela.\n");
            return WINDOW_FAILURE;
        }
        windows[windowId].actualListener = listenerId;
        windows[windowId].firstListener = listenerId;
    } else {
        if (listener_addToWindow(windows[windowId].actualListener, listenerId, windowId) == LISTENER_FAILURE) {
            __E("Nao foi possivel inserir o listener na tela.\n");
            return WINDOW_FAILURE;
        }
        windows[windowId].actualListener = listenerId;
        return WINDOW_SUCCESS;
    }
}

signed int window_findListenerId(signed int windowId, enum event_type event) {
    signed int aux = windows[windowId].firstListener;
    if (aux != -1) {
        do {
            if (listener_getEventType(aux) == event) {
                return aux;
            } else {
                aux = listener_next(aux);
            }
        } while (aux != windows[windowId].firstListener);
    }
    return WINDOW_FAILURE;
}

signed int window_insertImage(signed int windowId, signed int imageId) {
    signed int actual_image, first_image;

    if (window_scanArea(windowId, image_getX(imageId), image_getY(imageId), image_getWidth(imageId),
            image_getHeight(imageId))) {
        __E("A imagem não foi inserida na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstImage == -1) {
        actual_image = imageId;
        first_image = imageId;
    } else {
        actual_image = windows[windowId].actualImage;
        first_image = windows[windowId].firstImage;
    }

    if (image_addToWindow(actual_image, imageId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir a imagem na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualImage = imageId;
    windows[windowId].firstImage = first_image;

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_insertLabel(signed int windowId, signed int labelId) {
    signed int actual_label, first_label;

    if (window_scanArea(windowId, label_getX(labelId), label_getY(labelId), label_getWidth(labelId),
            label_getHeight(labelId))) {
        __E("O label não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstLabel == -1) {
        actual_label = labelId;
        first_label = labelId;
    } else {
        actual_label = windows[windowId].actualLabel;
        first_label = windows[windowId].firstLabel;
    }

    if (label_addToWindow(actual_label, labelId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o label na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualLabel = labelId;
    windows[windowId].firstLabel = first_label;

    return WINDOW_SUCCESS;
}

signed int window_insertListbox(signed int windowId, signed int listboxId) {
    int actual_listbox, first_listbox;

    if (window_scanArea(windowId, listbox_getX(listboxId), listbox_getY(listboxId), listbox_getWidth(listboxId),
            listbox_getHeight(listboxId))) {
        __E("O listbox não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstListbox == -1) {
        actual_listbox = listboxId;
        first_listbox = listboxId;
    } else {
        actual_listbox = windows[windowId].actualListbox;
        first_listbox = windows[windowId].firstListbox;
    }

    if (listbox_addToWindow(actual_listbox, listboxId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o listbox na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualListbox = listboxId;
    windows[windowId].firstListbox = first_listbox;

    return WINDOW_SUCCESS;
}

signed int window_inform_actualListbox(signed int windowId) {
    if (windows[windowId].actualListbox == -1) {
        return WINDOW_FAILURE;
    } else {
        return windows[windowId].actualListbox;
    }
}

signed int window_insertMultieditbox(signed int windowId, signed int multieditboxId) {
    signed int actual_multieditbox, first_multieditbox;

    if (window_scanArea(windowId, multieditbox_getX(multieditboxId), multieditbox_getY(multieditboxId), multieditbox_getWidth(multieditboxId),
            multieditbox_getHeight(multieditboxId))) {
        __E("O multieditbox não foi inserido na janela. Não é possível sobrepor componentes.\n");
        return WINDOW_FAILURE;
    }

    if (windows[windowId].firstMultieditbox == -1) {
        actual_multieditbox = multieditboxId;
        first_multieditbox = multieditboxId;
    } else {
        actual_multieditbox = windows[windowId].actualMultieditbox;
        first_multieditbox = windows[windowId].firstMultieditbox;
    }

    if (multieditbox_addToWindow(actual_multieditbox, multieditboxId, windowId, &windows[windowId]) == -1) {
        __E("Nao foi possivel inserir o multieditbox na tela.\n");
        return WINDOW_FAILURE;
    }

    windows[windowId].actualMultieditbox = multieditboxId;
    windows[windowId].firstMultieditbox = first_multieditbox;

    return WINDOW_SUCCESS;
}

/*
signed int window_inform_actualMultieditbox_selected(signed int windowId) {
    if (windows[windowId].actualMultieditbox_se == -1) {
        return WINDOW_FAILURE;
    }

    if (editbox_read(windows[windowId].actualEditbox_selected) == 1) {
        return windows[windowId].actualEditbox_selected;

    } else {
        return WINDOW_FAILURE;
    }
}
*/

void window_printScreen() {
    FILE *f = fopen("/root/teste.bmp", "r+b");
    unsigned short t = 0xFFFF;

    if (!f) {
        printf("erro ao abrir o arquivo\n");
    } else {
        printf("else\n");
        fseek(f, 0x36, SEEK_SET);
        // por enquanto, usando apenas um buffer
        //unsigned short *buffer16 = rgb_buffers[rgb_actualBuf];
        unsigned short *buffer16 = rgb_buffers[0];
        int coluna, linha, r, g, b;
        printf("setando buffer16...\n");

        //buffer16 += D1_HEIGHT * D1_WIDTH - 2 * D1_WIDTH;
        buffer16 += D1_HEIGHT * D1_WIDTH - D1_WIDTH;

        printf("antes dos for\n");

        for (linha = D1_HEIGHT - 1; linha >= 0; linha--) {
            for (coluna = 0; coluna < D1_WIDTH; coluna++) {
                printf("%x ", *(buffer16 + coluna));
                fwrite(buffer16 + coluna, sizeof (short), 1, f);
                //fwrite(&t, sizeof (short), 1, f);
            }
            buffer16 -= D1_WIDTH;
        }
        fclose(f);
        printf("Salvou a tela\n");
    }
}

static unsigned int window_scanArea(signed int windowId, unsigned int x, unsigned int y, unsigned int width, unsigned int height) {

    if (windows[windowId].firstImage != -1) {
        signed int aux = windows[windowId].firstImage;
        do {
            if (aux != windows[windowId].backgroundImageId) {
                if (window_rectangleIntersects(x, y, width, height, image_getX(aux), image_getY(aux),
                        image_getWidth(aux), image_getHeight(aux))) {
                    return 1;
                }
            }
            aux = image_next(aux);
        } while (windows[windowId].firstImage != aux);
    }

    if (windows[windowId].firstButton != -1) {
        signed int aux = windows[windowId].firstButton;
        do {
            if (window_rectangleIntersects(x, y, width, height, button_getX(aux), button_getY(aux),
                    button_getWidth(aux), button_getHeight(aux))) {
                return 1;
            }
            aux = button_next(aux);
        } while (windows[windowId].firstButton != aux);
    }

    if (windows[windowId].firstEditbox != -1) {
        signed int aux = windows[windowId].firstEditbox;
        do {
            if (window_rectangleIntersects(x, y, width, height, editbox_getX(aux), editbox_getY(aux),
                    editbox_getWidth(aux), editbox_getHeight(aux))) {
                return 1;
            }
            aux = editbox_next(aux);
        } while (windows[windowId].firstEditbox != aux);
    }

    if (windows[windowId].firstLabel != -1) {
        signed int aux = windows[windowId].firstLabel;
        do {
            if (window_rectangleIntersects(x, y, width, height, label_getX(aux), label_getY(aux),
                    label_getWidth(aux), label_getHeight(aux))) {
                return 1;
            }
            aux = label_next(aux);
        } while (windows[windowId].firstLabel != aux);
    }

    if (windows[windowId].firstListbox != -1) {
        signed int aux = windows[windowId].firstListbox;
        do {
            if (window_rectangleIntersects(x, y, width, height, listbox_getX(aux), listbox_getY(aux),
                    listbox_getWidth(aux), listbox_getHeight(aux))) {
                return 1;
            }
            aux = listbox_next(aux);
        } while (windows[windowId].firstListbox != aux);
    }

    return 0;
}

static unsigned int window_rectangleIntersects(unsigned int x1, unsigned int y1, unsigned int w1, unsigned int h1,
        unsigned int x2, unsigned int y2, unsigned int w2, unsigned int h2) {

    signed int flag_x = 0;

    if (x1 <= x2) {
        if (x2 < (x1 + w1)) {
            flag_x = 1;
        }
    } else {
        if (x1 < (x2 + w2)) {
            flag_x = 1;
        }
    }

    if (flag_x) {
        if (y1 <= y2) {
            if (y2 < (y1 + h1)) {
                return 1;
            }
        } else {
            if (y1 < (y2 + h2)) {
                return 1;
            }
        }
    }

    return 0;
}

void window_setComponentsRedraw(signed int windowId) {
    if (windowId < 0 || windowId >= WINDOW_MAX_NUMBER) {
        return;
    }
    
    windows[windowId].backgroundColorRedraw = 1;

    image_setRedraw(windows[windowId].backgroundImageId);

    if (windows[windowId].firstButton != -1) {
        signed int aux = windows[windowId].firstButton;
        do {
            button_setRedraw(aux);
            aux = button_next(aux);
        } while (windows[windowId].firstButton != aux);
    }

    if (windows[windowId].firstEditbox != -1) {
        signed int aux = windows[windowId].firstEditbox;
        do {
            editbox_setRedraw(aux);
            aux = editbox_next(aux);
        } while (windows[windowId].firstEditbox != aux);
    }

    if (windows[windowId].firstImage != -1) {
        signed int aux = windows[windowId].firstImage;
        do {
            image_setRedraw(aux);
            aux = image_next(aux);
        } while (windows[windowId].firstImage != aux);
    }

    if (windows[windowId].firstLabel != -1) {
        signed int aux = windows[windowId].firstLabel;
        do {
            label_setRedraw(aux);
            aux = label_next(aux);
        } while (windows[windowId].firstLabel != aux);
    }

    if (windows[windowId].firstListbox != -1) {
        signed int aux = windows[windowId].firstListbox;
        do {
            listbox_setRedraw(aux);
            aux = listbox_next(aux);
        } while (windows[windowId].firstListbox != aux);
    }
}

signed int window_removeButton(signed int windowId, signed int buttonId) {
    if (button_isSelected(buttonId)) {
        button_unselect(buttonId);
    }

    if (button_delFromWindow(buttonId) == BUTTON_FAILURE) {
        __E("Não foi possível remover o botao da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeEditbox(signed int windowId, signed int editboxId) {
    if (editbox_isSelected(editboxId)) {
        editbox_unselect(editboxId);
    }

    if (editbox_delFromWindow(editboxId) == EDITBOX_FAILURE) {
        __E("Não foi possível remover o editbox da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeIcon(signed int windowId, signed int iconId) {
    if (icon_isSelected(iconId)) {
        icon_unselect(iconId);
    }
    
    if (icon_delFromWindow(iconId) == ICON_FAILURE) {
        __E("Não foi possível remover o icone da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeImage(signed int windowId, signed int imageId) {
    if (image_delFromWindow(imageId) == IMAGE_FAILURE) {
        __E("Não foi possível remover a imagem da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeLabel(signed int windowId, signed int labelId) {
    if (label_delFromWindow(labelId) == LABEL_FAILURE) {
        __E("Não foi possível remover o label da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeListbox(signed int windowId, signed int listboxId) {
    if (listbox_delFromWindow(listboxId) == LISTBOX_FAILURE) {
        __E("Não foi possível remover o listbox da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeListener(signed int windowId, signed int listenerId) {
    if (listener_delFromWindow(listenerId) == LISTENER_FAILURE) {
        __E("Não foi possível remover o listener da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}

signed int window_removeMultieditbox(signed int windowId, signed int multieditboxId) {
    if (multieditbox_delFromWindow(multieditboxId) == LISTENER_FAILURE) {
        __E("Não foi possível remover o multieditbox da janela.\n");
        return WINDOW_FAILURE;
    }

    window_setComponentsRedraw(windowId);

    return WINDOW_SUCCESS;
}
